﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Client.Service
{
    using Domain.ClientService;
    using Domain.Common.Site;
    using Domain.Configuration;
    using Domain.DataService;
    using Domain.Factory;
    using Domain.IRepositories;
    using Domain.Models;
    using Domain.Schedule;
    using SoccerBet2014.Common.Collection;
    using SoccerBet2014.Common.Errors;
    using Domain.Models.Site;

    public class BetSiteProxyService : IBetSiteProxyService
    {
        private readonly IBetSiteManagerFactory managerFactory;
        private readonly IScheduleTimerFactory timerFactory;
        private readonly IBookmakerRepository bmRepository;
        private readonly IOddsRepository oddsRepository;
        private readonly IGameRepository gameRepository;
        private readonly ConcurrentDictionary<string, SessionManagerBase> managerCollection;
        private readonly ScheduleSettingConfiguration configurationSetting;
        private readonly IBetSiteServiceCallback betSiteCallback;

        private string GenKey(byte bmId, string account)
        {
            return string.Format("{0}|{1}", bmId, account.ToLower());
        }

        public BetSiteProxyService(IBetSiteManagerFactory _managerFactory
                                            , IScheduleTimerFactory _timerFactory
                                            , IBookmakerRepository _bmRepository
                                            , IOddsRepository _oddsRepository
                                            , IGameRepository _gameRepository
                                            , IBetSiteServiceCallback _betSiteCallback)
        {
            managerFactory = _managerFactory;
            timerFactory = _timerFactory;
            bmRepository = _bmRepository;
            oddsRepository = _oddsRepository;
            gameRepository = _gameRepository;
            betSiteCallback = _betSiteCallback;
            managerCollection = new ConcurrentDictionary<string, SessionManagerBase>();
            configurationSetting = (ScheduleSettingConfiguration)System.Configuration.ConfigurationManager.GetSection("bookmaker.schedule");
        }

        private void Manager_OnScheduleRequestOver(IBetSiteManagerService sender, BetSiteRequestType type, byte bmId, string account)
        {
            if (betSiteCallback != null)
            {
                betSiteCallback.OnSiteLogChange(bmId, account, type, DateTime.Now);
            }
        }

        private void Manager_OnScheduleRequestBefore(IBetSiteManagerService sender, BetSiteRequestType type, byte bmId, string account)
        {
            if (betSiteCallback != null)
            {
                betSiteCallback.OnSiteLogChange(bmId, account, type, DateTime.Now);
            }
        }

        public async Task<bool> Register(byte bmId, string account)
        {
            var bookmaker = bmRepository.Get(bmId);
            bookmaker.IsNull().ThrowInvalidOperation("不存在的博彩网站，检查bmId是否正确");

            var key = GenKey(bmId, account);
            if (managerCollection.ContainsKey(key)) return true;
            var manager = managerFactory.CreateOrGet(bmId, account, bookmaker.DefaultHost);
            var sessionManager = manager as SessionManagerBase;
            sessionManager.IsNull().ThrowInvalidOperation("网站管理程序不合法");
            managerCollection.TryAdd(key, sessionManager);
            return true;
        }

        public async Task<bool> Signup(byte bmId, string account, string password)
        {
            var key = GenKey(bmId, account);
            managerCollection.ContainsKey(key)
                .IsFalse().ThrowInvalidOperation("该账户{0}未注册，请先调用Register方法", account);

            var manager = managerCollection[key];
            if (manager.LoginInfo.IsLogin) return true;
            if (manager.ValidSessionOnline(DateTime.Now.AddMinutes(-3)))
            {
                manager.RecoverContext();
                var ckTask = manager.CheckSession();
                await ckTask;
                if (ckTask.Exception != null || ckTask.IsCanceled)
                    return false;
                if (ckTask.Result)
                {
                    await manager.RequestMainPage();
                    return true;
                }
            }

            var taskStart = manager.Start();
            await taskStart;
            if (taskStart.IsCanceled || taskStart.Exception != null)
                return false;
            var taskSignup = manager.Signup(password);
            await taskSignup;
            if (taskSignup.IsCanceled || taskSignup.Exception != null)
                return false;
            return taskSignup.Result;
        }

        public async Task Sigout(byte bmId, string account)
        {
            var key = GenKey(bmId, account);
            if (!managerCollection.ContainsKey(key)) return;
            var manager = managerCollection[key];
            if (manager.LoginInfo.IsLogin)
                await manager.Signout();
        }

        public async Task StartSchedule(byte bmId, string account)
        {
            var key = GenKey(bmId, account);
            managerCollection.ContainsKey(key)
                        .IsFalse().ThrowInvalidOperation("该账户{0}未注册，请先调用Register方法", account);
            var manager = managerCollection[key];
            var timer = timerFactory.CreateOrGet();
            var config = configurationSetting.SiteBmConfiguration.FirstOrDefault(c => c.BmID == bmId);
            manager.OnScheduleRequestBefore += Manager_OnScheduleRequestBefore;
            manager.OnScheduleRequestOver += Manager_OnScheduleRequestOver;
            manager.SetSchedule(timer, config);
            manager.StartSchedule();
        }

        public async Task StopSchedule(byte bmId, string account)
        {
            var key = GenKey(bmId, account);
            if (!managerCollection.ContainsKey(key)) return;
            var manager = managerCollection[key];
            manager.StopSchedule();
        }

        public async Task<BetTick> RequestTick(byte bmId, string gameId, Odds.MarketType type, OddsPrice price, decimal handicap, string remoteOddsId)
        {
            var oddsRange = oddsRepository.GetMany(o => o.BmID == bmId
                                                                            && o.GameID == gameId
                                                                            && o.Type == type
                                                                            && o.OddsID == remoteOddsId);

            //var mygame = gameRepository.GetMany(o => o.BmID == bmId && o.GameID == gameId);
            //var mygame1 = gameRepository.GetMany(o => o.BmID == bmId && o.StartTime >= new DateTime(2014, 6, 28, 23, 59, 0) && o.StartTime < new DateTime(2014, 6, 29, 00, 10, 0));
            //var myoddsRange = oddsRepository.GetMany(o => o.BmID == bmId && o.GameID == gameId);
            if (oddsRange == null || oddsRange.IsEmpty())
            {
                oddsRange = oddsRepository.GetMany(o => o.BmID == bmId
                                                                            && o.GameID == gameId
                                                                            && o.Type == type
                                                                            && o.Handicap == handicap);

            }
            if (oddsRange == null || oddsRange.IsEmpty())
            {
                throw new Exception(string.Format("没有匹配的Odds赔率，请检查Bm-{0}登录", bmId));
            }
            var query = from mg in managerCollection.Values
                        join od in oddsRange on new { mg.BookerID, mg.Account } equals new { BookerID = od.BmID, Account = od.LastUpdateAccount }
                        where mg.LoginInfo.IsLogin
                        orderby od.LastUpdateTime descending
                        select new
                        {
                            SiteManager = mg,
                            Odds = od
                        };
            if (query.IsEmpty())
                Error.ThrowInvalidOperation("没有登录");

            var request = query.FirstOrDefault();
            var odds = request.Odds;
            var manager = request.SiteManager;
            var task = manager.RequestTick(odds.OddsID, price);
            await task;
            if (task.IsCanceled || task.Exception != null)
                return null;
            return task.Result;
        }

        public async Task<BetTick> RequestTick(BetTick old, bool reset)
        {
            var manager = managerCollection.Values.FirstOrDefault(m => m.BookerID == old.BmID && m.Account.Equals(old.UpdateAccount, StringComparison.OrdinalIgnoreCase));
            manager.IsNull().ThrowInvalidOperation("位于{1}旧的请求已经失效，请重新登录{0}", old.UpdateAccount, old.UpdateHost);
            manager.LoginInfo.IsLogin.IsFalse().ThrowInvalidOperation("位于{1}旧的请求已经失效，请重新登录{0}", old.UpdateAccount, old.UpdateHost);
            Task<BetTick> task = null;
            if (reset)
            {
                var price = new OddsPrice(old.Price, old.PriceType);
                price.MkType = old.MarketType;
                task = manager.RequestTick(old.OddsID, price);
            }
            else
                task = manager.RequestTick(old);
            await task;
            if (task.IsCompleted)
                return task.Result;
            return null;
        }

        public async Task<bool> HasLogin(byte bmId)
        {
            return managerCollection.Any(m => m.Value.LoginInfo.IsLogin && m.Value.BookerID == bmId);
        }

        public async Task<BettingResult> RequestBetting(BetTick tick, decimal money)
        {
            var manager = managerCollection.Values.FirstOrDefault(m => m.BookerID == tick.BmID && m.Account.Equals(tick.UpdateAccount, StringComparison.OrdinalIgnoreCase));
            manager.IsNull().ThrowInvalidOperation("位于{1}旧的请求已经失效，请重新登录{0}", tick.UpdateAccount, tick.UpdateHost);
            manager.LoginInfo.IsLogin.IsFalse().ThrowInvalidOperation("位于{1}旧的请求已经失效，请重新登录{0}", tick.UpdateAccount, tick.UpdateHost);
            if (money > tick.MaxAmount || money < tick.MinAmount)
                return new BettingResult() { Message = "投注金额超额", State = BettingState.Failed };

            var task = manager.RequestBetting(tick, money);
            return await task;
        }

        public async Task<IList<BettingOrder>> RequestAllOrder()
        {
            var query = managerCollection.Values.Where(m => m.LoginInfo.IsLogin);
            List<BettingOrder> orders = new List<BettingOrder>();
            if (!query.Any()) return orders;
            foreach (var manager in query)
            {
                await manager.RequestBettingHisOrder().ContinueWith(t =>
                {
                    if (t.IsCanceled || t.Exception != null) return;
                    if (t.Result == null || !t.Result.Any()) return;
                    orders.AddRange(t.Result);
                });
            }
            return orders;
        }

    }
}
